/**
 * 20220113: created by jordia
 */
import XRConstants from "./xr-constants";
import XRKeymap from "./xr-keymap";

const EVENT_TYPE={
    /**
     * common
     */
    pointMove:"pointMove",
    pointDown:"pointDown",
    pointUp:"pointUp",
    pointClick:"pointClick",
    pointDoubleClick:"pointDoubleClick",
    leftPointDown:"leftPointDown",
    leftPointUp:"leftPointUp",
    leftPointMove:"leftPointMove",
    rightPointDown:"rightPointDown",
    rightPointUp:"rightPointUp",
    rightPointMove:"rightPointMove",
    /**
     * exclusive
     */
    sessionStart:'sessionStart',
    sessionEnd:'sessionEnd',
    leftPointClick:'leftPointClick',
    leftPointDoubleClick:'leftPointDoubleClick',
    rightPointClick:'rightPointClick',
    rightPointDoubleClick:'rightPointDoubleClick',
    squeeze:'squeeze',
    leftSqueeze:'leftSqueeze',
    rightSqueeze:'rightSqueeze',
    squeezeStart:'squeezeStart',
    leftSqueezeStart:'leftSqueezeStart',
    rightSqueezeStart:'rightSqueezeStart',
    squeezeEnd:'squeezeEnd',
    rightSqueezeEnd:'rightSqueezeEnd',
    leftSqueezeEnd:'leftSqueezeEnd',
    sustain:'sustain',
    connected:'connected',
    disconnected:'disconnected',
    /**
     * gamepad
     */
    gamepadLeftTrigger:XRConstants.GAMEPAD_BUTTON_LEFT_TRIGGER,
    gamepadLeftSqueeze:XRConstants.GAMEPAD_BUTTON_LEFT_SQUEEZE,
    gamepadLeftThumbStick:XRConstants.GAMEPAD_BUTTON_LEFT_THUMB_STICK,
    gamepadGenericX:XRConstants.GAMEPAD_BUTTON_GENERIC_X,
    gamepadGenericY:XRConstants.GAMEPAD_BUTTON_GENERIC_Y,
    gamepadLeftThumbStickX:XRConstants.GAMEPAD_BUTTON_LEFT_THUMB_STICK_X,
    gamepadLeftThumbStickY:XRConstants.GAMEPAD_BUTTON_LEFT_THUMB_STICK_Y,
    gamepadRightTrigger:XRConstants.GAMEPAD_BUTTON_RIGHT_TRIGGER,
    gamepadRightSqueeze:XRConstants.GAMEPAD_BUTTON_RIGHT_SQUEEZE,
    gamepadRightThumbStick:XRConstants.GAMEPAD_BUTTON_RIGHT_THUMB_STICK,
    gamepadGenericA:XRConstants.GAMEPAD_BUTTON_GENERIC_A,
    gamepadGenericB:XRConstants.GAMEPAD_BUTTON_GENERIC_B,
    gamepadRightThumbStickX:XRConstants.GAMEPAD_BUTTON_RIGHT_THUMB_STICK_X,
    gamepadRightThumbStickY:XRConstants.GAMEPAD_BUTTON_RIGHT_THUMB_STICK_Y,
};

/**
 * integrated XR-event center
 */
export default class XREvent {
    constructor(xrBase){
        this._xr=xrBase;
        this._init();
    }

    //....................................................public.......................................................
    /**
     * all possible event types
     * @returns {object}
     */
    static get types(){
        return EVENT_TYPE;
    }

    /**
     * keep sustain-event dispatching or not(gamepad events need this enabled)
     * @param bol
     */
    set sustain(bol){
        this._sustain=bol;
    }

    /**
     * double click interval
     * @param num
     */
    set doubleClickInterval(num){
        this._doubleInterval=Math.max(num,40);
    }

    /**
     * enable/disable right double click
     * @param bol
     */
    set rightDoubleClickEnabled(bol){
        this._enableDoubleRight=!!bol;
    }

    /**
     * enable/disable left double click
     * @param bol
     */
    set leftDoubleClickEnabled(bol){
        this._enableDoubleLeft=!!bol;
    }

    /**
     * add event listener
     * @param eventType
     * @param eventHandle
     */
    addEventListener(eventType,eventHandle){
        if(this._eventContainer[eventType].indexOf(eventHandle)!==-1)return;
        this._eventContainer[eventType].push(eventHandle);
    }

    /**
     * remove event listener
     * @param eventType
     * @param eventHandle
     */
    removeEventListener(eventType,eventHandle){
        let index=this._eventContainer[eventType].indexOf(eventHandle);
        if(index<0)return;
        this._eventContainer[eventType].splice(index,1);
    }

    /**
     * begin to transform events
     */
    start(){
        if(this._started)return;
        this._started=true;
        let xr=this._xr;
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_RAY_MOVECHANGE,this._onMoveChange);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_RAY_MOVE,this._onSustain);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_HAND_PINCHSTART,this._onPinchStart);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_HAND_PINCHEND,this._onPinchEnd);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_CONNECTED,this._onConnected);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_DISCONNECTED,this._onDisconnected);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SELECT,this._onSelect);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SELECTSTART,this._onSelectStart);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SELECTEND,this._onSelectEnd);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SQUEEZE,this._onSqueeze);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SQUEEZESTART,this._onSqueezeStart);
        xr.eventCenter.addEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SQUEEZEEND,this._onSqueezeEnd);
        xr.eventCenter.addEventListener(XRConstants.MANAGER_EVENT_SESSIONSTART,this._onSessionStart);
        xr.eventCenter.addEventListener(XRConstants.MANAGER_EVENT_SESSIONEND,this._onSessionEnd);
    }

    /**
     * stop transform events
     */
    end(){
        if(!this._started)return;
        this._started=false;
        let xr=this._xr;
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_RAY_MOVECHANGE,this._onMoveChange);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_RAY_MOVE,this._onSustain);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_HAND_PINCHSTART,this._onPinchStart);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_HAND_PINCHEND,this._onPinchEnd);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_CONNECTED,this._onConnected);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_DISCONNECTED,this._onDisconnected);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SELECT,this._onSelect);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SELECTSTART,this._onSelectStart);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SELECTEND,this._onSelectEnd);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SQUEEZE,this._onSqueeze);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SQUEEZESTART,this._onSqueezeStart);
        xr.eventCenter.removeEventListener(XRConstants.CONTROLLER_EVENT_COMMON_SQUEEZEEND,this._onSqueezeEnd);
        xr.eventCenter.removeEventListener(XRConstants.MANAGER_EVENT_SESSIONSTART,this._onSessionStart);
        xr.eventCenter.removeEventListener(XRConstants.MANAGER_EVENT_SESSIONEND,this._onSessionEnd);
    }

    /**
     * clear event listener
     */
    clear(){
        for(let key in this._eventContainer){
            this._eventContainer[key+''].length=0;
        }
    }

    dispose(){
        this.end();
        this.clear();
    }

    //.....................................................private.....................................................

    _setEvent(data){
        let tar=data.paras.target;
        data.object=tar;
        if(tar.hasOwnProperty('userData')){
            if(tar.userData.hasOwnProperty('inputSource')){
                data.handedness=tar.userData.inputSource.handedness;
            }
        }
        return data;
    }
    _doEvent(eventType,eventData){
        for(let handle of this._eventContainer[eventType]){
            handle(this._setEvent(eventData));
        }
    }
    _init(){
        this._initVariable();
        this._initEventContainer();
        this._initEventBind();
    }
    _initVariable(){
        this._eventContainer=null;
        this._started=false;
        this._sustain=true;
        this._doubleInterval=500;
        this._clickTimerRight=null;
        this._clickCountRight=0;
        this._enableDoubleRight=true;
        this._clickTimerLeft=null;
        this._clickCountLeft=0;
        this._enableDoubleLeft=true;
        this._gamepadState={};
    }
    _initEventContainer(){
        this._eventContainer={};
        for(let key in EVENT_TYPE){
            this._eventContainer[key]=[];
        }
    }
    _initEventBind(){
        this._onMoveChange=(e)=>{
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.pointMove,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightPointMove,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftPointMove,e);
                    break;
            }
        };
        this._onPinchStart=(e)=>{
            /**
             * todo:confirm
             */
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.pointDown,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightPointDown,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftPointDown,e);
                    break;
            }
        };
        this._onPinchEnd=(e)=>{
            /**
             * todo:confirm
             */
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.pointUp,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightPointUp,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftPointUp,e);
                    break;
            }
        };
        this._onSelect=(e)=>{
            let tar=e.paras.target;
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    if(this._enableDoubleRight){
                        this._clickCountRight++;
                        if(this._clickCountRight>1)return;
                        this._clickTimerRight=setTimeout(()=>{
                            let double=this._clickCountRight>1;
                            this._clickCountRight=0;
                            this._doEvent(double?EVENT_TYPE.pointDoubleClick:EVENT_TYPE.pointClick,e);
                            this._doEvent(double?EVENT_TYPE.rightPointDoubleClick:EVENT_TYPE.rightPointClick,e);
                            clearTimeout(this._clickTimerRight);
                        },this._doubleInterval);
                    }else{
                        this._doEvent(EVENT_TYPE.pointClick,e);
                        this._doEvent(EVENT_TYPE.rightPointClick,e);
                    }
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    if(this._enableDoubleLeft){
                        this._clickCountLeft++;
                        if(this._clickCountLeft>1)return;
                        this._clickTimerLeft=setTimeout(()=>{
                            let double=this._clickCountLeft>1;
                            this._clickCountLeft=0;
                            this._doEvent(double?EVENT_TYPE.pointDoubleClick:EVENT_TYPE.pointClick,e);
                            this._doEvent(double?EVENT_TYPE.leftPointDoubleClick:EVENT_TYPE.leftPointClick,e);
                            clearTimeout(this._clickTimerLeft);
                        },this._doubleInterval);
                    }else{
                        this._doEvent(EVENT_TYPE.pointClick,e);
                        this._doEvent(EVENT_TYPE.leftPointClick,e);
                    }
                    break;
            }
        };
        this._onSelectStart=(e)=>{
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.pointDown,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightPointDown,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftPointDown,e);
                    break;
            }
        };
        this._onSelectEnd=(e)=>{
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.pointUp,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightPointUp,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftPointUp,e);
                    break;
            }
        };
        this._onSqueeze=(e)=>{
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.squeeze,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightSqueeze,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftSqueeze,e);
                    break;
            }
        };
        this._onSqueezeStart=(e)=>{
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.squeezeStart,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightSqueezeStart,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftSqueezeStart,e);
                    break;
            }
        };
        this._onSqueezeEnd=(e)=>{
            let tar=e.paras.target;
            this._doEvent(EVENT_TYPE.squeezeEnd,e);
            switch (tar.userData.inputSource.handedness) {
                case XRConstants.HANDEDNESS_RIGHT:
                    this._doEvent(EVENT_TYPE.rightSqueezeEnd,e);
                    break;
                case XRConstants.HANDEDNESS_LEFT:
                    this._doEvent(EVENT_TYPE.leftSqueezeEnd,e);
                    break;
            }
        };
        this._onSessionStart=(e)=>{
            this._doEvent(EVENT_TYPE.sessionStart,e);
        };
        this._onSessionEnd=(e)=>{
            this._doEvent(EVENT_TYPE.sessionEnd,e);
        };
        this._onConnected=(e)=>{
            this._doEvent(EVENT_TYPE.connected,e);
        };
        this._onDisconnected=(e)=>{
            this._doEvent(EVENT_TYPE.disconnected,e);
        };
        this._onSustain=(e)=>{
            if(!this._sustain)return;
            this._doEvent(EVENT_TYPE.sustain,e);
            let inputSource=e.paras.target.userData.inputSource;
            if(!inputSource)return;
            let gamepad=e.paras.target.userData.inputSource.gamepad;
            if(!gamepad)return;
            this._checkGamepad(XRKeymap(this._xr.deviceProfile,this._xr.controllerProfile,inputSource.handedness),gamepad,e);
        };
    }
    _checkGamepad(keymap,gamepad,event) {
        for(let k in keymap){
            let key=k+'';
            let op=keymap[key];
            let ref=this._gamepadState[key];
            let cur,btn;
            switch (op.gesture) {
                case XRConstants.GESTURE_TYPE_PRESS:
                    /**
                     * state: start,end
                     * possible values: .pressed, .value
                     */
                    btn=gamepad.buttons[op.index];
                    cur=btn.pressed;
                    if((!!ref)===cur)continue;
                    this._gamepadState[key]=cur;
                    event.pressed=btn.pressed;
                    event.value=btn.value;
                    this._doEvent(key,event);
                    break;
                case XRConstants.GESTURE_TYPE_PUSH:
                    /**
                     * state: change
                     * possible value: changed value
                     */
                    cur=gamepad.axes[op.index];
                    if((ref||0)===cur)continue;
                    this._gamepadState[key]=cur;
                    event.value=cur;
                    this._doEvent(key,event);
                    break;
                case XRConstants.GESTURE_TYPE_TOUCH:
                    /**
                     * todo:
                     *      may have start,move,end ?
                     *      possible values: .touched, .value
                     * btn=gamepad.buttons[op.index];
                     * cur=btn.touched||btn.value;
                     */
                    break;
            }
        }
    }
}

